<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\UX\LiveComponent\Test;

use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\UX\LiveComponent\Test\Util\AssertDispatchedEvent;
use Symfony\UX\LiveComponent\Test\Util\AssertEmittedEvent;
use Symfony\UX\TwigComponent\ComponentFactory;

/**
 * @author Kevin Bond <kevinbond@gmail.com>
 */
trait InteractsWithLiveComponents
{
    protected function createLiveComponent(string $name, array $data = [], ?KernelBrowser $client = null): TestLiveComponent
    {
        if (!$this instanceof KernelTestCase) {
            throw new \LogicException(\sprintf('The "%s" trait can only be used on "%s" classes.', __TRAIT__, KernelTestCase::class));
        }

        /** @var ComponentFactory $factory */
        $factory = self::getContainer()->get('ux.twig_component.component_factory');
        $metadata = $factory->metadataFor($name);

        if (!$metadata->get('live')) {
            throw new \LogicException(\sprintf('The "%s" component is not a live component.', $name));
        }

        return new TestLiveComponent(
            $metadata,
            $data,
            $factory,
            $client ?? self::getContainer()->get('test.client'),
            self::getContainer()->get('ux.live_component.component_hydrator'),
            self::getContainer()->get('ux.live_component.metadata_factory'),
            self::getContainer()->get('router'),
        );
    }

    protected function assertComponentEmitEvent(TestLiveComponent $testLiveComponent, string $expectedEventName): AssertEmittedEvent
    {
        $event = $testLiveComponent->getEmittedEvent($testLiveComponent->render(), $expectedEventName);

        self::assertNotNull($event, \sprintf('The component "%s" did not emit event "%s".', $testLiveComponent->getName(), $expectedEventName));

        return new AssertEmittedEvent($this, $event['event'], $event['data']);
    }

    protected function assertComponentNotEmitEvent(TestLiveComponent $testLiveComponent, string $eventName): void
    {
        self::assertNull($testLiveComponent->getEmittedEvent($testLiveComponent->render(), $eventName), \sprintf('The component "%s" did emit event "%s".', $testLiveComponent->getName(), $eventName));
    }

    protected function assertComponentDispatchBrowserEvent(TestLiveComponent $testLiveComponent, string $expectedEventName): AssertDispatchedEvent
    {
        $event = $testLiveComponent->getDispatchedBrowserEvent($testLiveComponent->render(), $expectedEventName);

        self::assertNotNull(
            $event,
            \sprintf('The component "%s" did no dispatch browser event "%s".', $testLiveComponent->getName(), $expectedEventName)
        );

        return new AssertDispatchedEvent($this, $event['event'], $event['payload']);
    }

    protected function assertComponentNotDispatchBrowserEvent(TestLiveComponent $testLiveComponent, string $eventName): void
    {
        self::assertNull(
            $testLiveComponent->getDispatchedBrowserEvent($testLiveComponent->render(), $eventName),
            \sprintf('The component "%s" did dispatch browser event "%s".', $testLiveComponent->getName(), $eventName)
        );
    }
}
